package com.evzgaga.evzrecon.pluginmanager;

import com.evzgaga.evzrecon.annotations.Injected;
import com.evzgaga.evzrecon.plugins.ChatLoggingPlugin;
import com.evzgaga.evzrecon.plugins.ScoresPlugin;
import com.evzgaga.evzrecon.xmlrpc.client.GbxRemoteConnection;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class PluginManager {

    private PluginLoader loader;
    private Class[] vitalPlugins = new Class[]{ChatLoggingPlugin.class, /*ChatCommandsPlugin.class, */
        /*TrackManagementPlugin.class,*/ ScoresPlugin.class};
    private List<Object> injectableInstances = new ArrayList<Object>();
    private final GbxRemoteConnection connection;

    public PluginManager(GbxRemoteConnection connection) {
        loader = new PluginLoader();
        this.connection = connection;
        addInjectableObject(connection);
    }

    public Object loadPlugin(String pluginpath) throws PluginInitializationException {
        Object plugin = loader.loadPlugin(pluginpath);

        connection.addAllServerCallbackListeners(loader.getInterestedCallbacksAndListeners(plugin));
        connection.addAllChatCommandListeners(loader.getInterestedChatCommandHandlersAndListeners(plugin));

        injectMembers(plugin);

        return plugin;
    }

    public void loadVitalPlugins() {
        for (Class pluginType : vitalPlugins) {
            try {
                Object plugin = pluginType.newInstance();
            } catch (InstantiationException ex) {
                Logger.getLogger(PluginManager.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalAccessException ex) {
                Logger.getLogger(PluginManager.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public final void addInjectableObject(Object obj) {
        if (obj != null) {
            injectableInstances.add(obj);
        }
    }

    public Object getInjectableObjectOfType(Class expectedType) {
        for (Object object : injectableInstances) {
            if (expectedType.isAssignableFrom(object.getClass())) {
                return object;
            }
        }

        return null;
    }

    protected void injectMembers(Object plugin) throws PluginInitializationException {
        for (Field field : plugin.getClass().getDeclaredFields()) {
            Injected annotation = field.getAnnotation(Injected.class);

            if (annotation != null) {
                Object injectableObject = getInjectableObjectOfType(field.getType());

                if ((injectableObject == null) && !annotation.canBeNull()) {
                    throw new PluginInitializationException("No non-null value could be injected into field "
                            + field.getName() + " of plugin " + plugin.getClass().getName());
                } else {
                    try {
                        field.set(plugin, injectableObject);
                    } catch (Exception ex) {
                        throw new PluginInitializationException("Could not inject field " + field.getName()
                                + " in plugin " + plugin.getClass().getName(), ex);
                    }
                }
            }
        }
    }
}
